]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
screenshot: Fix slow audiovisual feedback on when taking screenshot
authorJonas Dreßler <verdre@v0yd.nl>
Tue, 9 Feb 2021 11:51:33 +0000 (12:51 +0100)
committerMarge Bot <marge-bot@gnome.org>
Thu, 11 Feb 2021 18:51:40 +0000 (18:51 +0000)
Add a "screenshot-taken" signal from the screenshot service's internal C
implementation, and use that to trigger the camera flash visual effect
and the click sound, allowing them to run in parallel with the PNG
compression instead of waiting until the file is complete to start.

This significantly improves perceived latency on high res setups such as
4K, 5K, or dual 4K screens.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/512

Co-authored-by: Brion Vibber <bvibber@wikimedia.org>
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1658>

js/ui/screenshot.js
po/POTFILES.in
src/shell-screenshot.c

index 1c724f0babd33f135d5a1e627b85b18f857ec295..59a6c46aff60d9b5be84edce57c3937024d515bb 100644 (file)
@@ -131,16 +131,19 @@ var ScreenshotService = class {
         return [null, null];
     }
 
-    _onScreenshotComplete(area, stream, file, flash, invocation) {
-        if (flash) {
-            let flashspot = new Flashspot(area);
-            flashspot.fire(() => {
-                this._removeShooterForSender(invocation.get_sender());
+    _flashAsync(shooter) {
+        return new Promise((resolve, _reject) => {
+            shooter.connect('screenshot_taken', (s, area) => {
+                const flashspot = new Flashspot(area);
+                flashspot.fire(resolve);
+
+                global.display.get_sound_player().play_from_theme(
+                    'screen-capture', _('Screenshot taken'), null);
             });
-        } else {
-            this._removeShooterForSender(invocation.get_sender());
-        }
+        });
+    }
 
+    _onScreenshotComplete(stream, file, invocation) {
         stream.close(null);
 
         let filenameUsed = '';
@@ -192,9 +195,12 @@ var ScreenshotService = class {
             return;
 
         try {
-            let [area] =
-                await screenshot.screenshot_area(x, y, width, height, stream);
-            this._onScreenshotComplete(area, stream, file, flash, invocation);
+            await Promise.all([
+                flash ? this._flashAsync(screenshot) : null,
+                screenshot.screenshot_area(x, y, width, height, stream),
+            ]);
+            this._onScreenshotComplete(stream, file, invocation);
+            this._removeShooterForSender(invocation.get_sender());
         } catch (e) {
             this._removeShooterForSender(invocation.get_sender());
             invocation.return_value(new GLib.Variant('(bs)', [false, '']));
@@ -212,9 +218,12 @@ var ScreenshotService = class {
             return;
 
         try {
-            let [area] =
-                await screenshot.screenshot_window(includeFrame, includeCursor, stream);
-            this._onScreenshotComplete(area, stream, file, flash, invocation);
+            await Promise.all([
+                flash ? this._flashAsync(screenshot) : null,
+                screenshot.screenshot_window(includeFrame, includeCursor, stream),
+            ]);
+            this._onScreenshotComplete(stream, file, invocation);
+            this._removeShooterForSender(invocation.get_sender());
         } catch (e) {
             this._removeShooterForSender(invocation.get_sender());
             invocation.return_value(new GLib.Variant('(bs)', [false, '']));
@@ -232,8 +241,12 @@ var ScreenshotService = class {
             return;
 
         try {
-            let [area] = await screenshot.screenshot(includeCursor, stream);
-            this._onScreenshotComplete(area, stream, file, flash, invocation);
+            await Promise.all([
+                flash ? this._flashAsync(screenshot) : null,
+                screenshot.screenshot(includeCursor, stream),
+            ]);
+            this._onScreenshotComplete(stream, file, invocation);
+            this._removeShooterForSender(invocation.get_sender());
         } catch (e) {
             this._removeShooterForSender(invocation.get_sender());
             invocation.return_value(new GLib.Variant('(bs)', [false, '']));
index 4f83b62cf72745c40d28d8daca009828481d2cde..37f65690332afefa812d9e9009781089ad74a32e 100644 (file)
@@ -47,6 +47,7 @@ js/ui/panel.js
 js/ui/popupMenu.js
 js/ui/runDialog.js
 js/ui/screenShield.js
+js/ui/screenshot.js
 js/ui/search.js
 js/ui/shellEntry.js
 js/ui/shellMountOperation.js
index 321a82ec7d16b5fe3e23f5cb983fdf8bb19c1eee..100429f2172b910aca6a01c789dc31d991f60498 100644 (file)
@@ -25,6 +25,15 @@ typedef enum _ShellScreenshotMode
   SHELL_SCREENSHOT_AREA,
 } ShellScreenshotMode;
 
+enum
+{
+  SCREENSHOT_TAKEN,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
 typedef struct _ShellScreenshotPrivate  ShellScreenshotPrivate;
 
 struct _ShellScreenshot
@@ -55,7 +64,15 @@ G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
 static void
 shell_screenshot_class_init (ShellScreenshotClass *screenshot_class)
 {
-  (void) screenshot_class;
+  signals[SCREENSHOT_TAKEN] =
+    g_signal_new ("screenshot-taken",
+                  G_TYPE_FROM_CLASS(screenshot_class),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1,
+                  META_TYPE_RECTANGLE);
 }
 
 static void
@@ -321,6 +338,8 @@ grab_window_screenshot (ShellScreenshot     *screenshot,
       draw_cursor_image (priv->image, priv->screenshot_area);
     }
 
+  g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0, &rect);
+
   task = g_task_new (screenshot, NULL, on_screenshot_written, result);
   g_task_run_in_thread (task, write_screenshot_thread);
   g_object_unref (task);
@@ -372,6 +391,9 @@ on_after_paint (ClutterStage     *stage,
       grab_screenshot (screenshot, priv->flags, result);
     }
 
+  g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+                 (cairo_rectangle_int_t *) &priv->screenshot_area);
+
   meta_enable_unredirect_for_display (display);
 }
 
@@ -430,6 +452,9 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
   if (meta_is_wayland_compositor ())
     {
       grab_screenshot (screenshot, flags, result);
+
+      g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+                     (cairo_rectangle_int_t *) &priv->screenshot_area);
     }
   else
     {
@@ -540,6 +565,9 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
                           priv->screenshot_area.height,
                           SHELL_SCREENSHOT_FLAG_NONE);
 
+      g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+                     (cairo_rectangle_int_t *) &priv->screenshot_area);
+
       task = g_task_new (screenshot, NULL, on_screenshot_written, result);
       g_task_run_in_thread (task, write_screenshot_thread);
     }